/*
 * instr_list.c
 *
 * Group: Fantastic Five
 * Date: 4/25/08
 *
 * Description: Defines a linked list to store parsed instructions (used by parser)
 */

#include "instr_list.h"

/* Creates and initializes a new instruction list */
struct instr_list * new_instr_list()
{
    struct instr_list *new_il = (struct instr_list *)malloc(sizeof(struct instr_list));
    new_il->head = NULL;
    new_il->tail = NULL;
    new_il->count = 0;

    return new_il;
}

/* Creates and initializes a new instr */
struct instr * new_instr(int linenum, enum opcode op, union value val1, enum lexeme type1, union value val2, enum lexeme type2)
{
    struct instr *new_instr = (struct instr *)malloc(sizeof(struct instr));
    new_instr->next = NULL;
    new_instr->prev = NULL;
    new_instr->op = op;
    new_instr->linenum = linenum;
    new_instr->v1 = val1;
    new_instr->type1 = type1;
    new_instr->v2 = val2;
    new_instr->type2 = type2;

    return new_instr;
}

/* Pushes new instr i onto back of instruction list */
void push_instr(struct instr *i, struct instr_list *il)
{
    if(il->head == NULL || il->tail == NULL) /* check for empty list */
    { /* initialize head and tail pointers to new instr */
        i->next = NULL;
        i->prev = NULL;
        il->tail = i;
        il->head = i;
        il->count = 1;
    }
    else /* push instr onto back of list */
    {
        i->next = NULL;
        i->prev = il->tail;
        i->prev->next = i;
        il->tail = i;
        il->count++;
    }
}

/* Pops off instr at beginning of instruction list */
struct instr * pop_instr(struct instr_list *il)
{
    struct instr *temp = il->head;

    if(temp == NULL || il->count == 0) /* check for invalid list */
    {
        fprintf(stderr, "No instructions to pop off list.\n");
    }
    else if(il->head == il->tail) /* check for list with single item */
    { /* change all list pointers to null and adjust count */
        il->head = NULL;
        il->tail = NULL;
        il->count = 0;
    }
    else /* pop first instr off list, adjust head pointer and count */
    {
        il->head = il->head->next;
        il->head->prev = NULL;
        il->count--;
    }

    return temp;
}

/* Removes instr rem from instruction list */
void remove_instr(struct instr *rem, struct instr_list *il)
{
    if(il->count == 0 || il == NULL) /* Check for empty list */
    {
        fprintf(stderr, "Cannot remove instr from list.\n");
        return;
    }
    else if(il->count == 1 || il->head == rem) /* check if only one instr in list or instr is head, pop instr off if so */
    {
        pop_instr(il);
    }
    else if(il->tail == rem) /* check if instr to be removed is tail, adjust tail and count if so */
    {
        il->tail = rem->prev;
        il->tail->next = NULL;
        il->count--;
        free(rem);
        rem = NULL;
    }
    else /* remove instr from list and adjust necessary pointers */
    {
        rem->prev->next = rem->next;
        rem->next->prev = rem->prev;
        il->count--;
        free(rem);
        rem = NULL;
    }
}

/* Checks to see if list contains instructions, return 1 if empty, 0 if not */
int no_instr(struct instr_list *il)
{
    if(il->count > 0)
        return 0;
    else
        return 1;
}

/* Removes all instructions from instruction list */
void empty_instr_list(struct instr_list *il)
{
    struct instr *cur = il->head;

    if(cur == NULL || il->count == 0)
    {
        return;
    }
    while(il->count > 0) /* pop all instructions off */
    {
        cur = pop_instr(il);
        free(cur);
    }
}
